大家好,今天終於來到第十五天啦!前面介紹了很多TypeScript(TS)自己獨有的特色,像:型別推論、型別收窄等等,也介紹了程式基礎的語法:陣列、函數等等,今天則要來談談Class類別!但是,在那之前,先來了解與之相關的物件導向程式設計(OOP)。
英文小學堂:
OOP全名:Object-Oriented Programming 物件導向程式設計
在之前介紹TS入門的文章中,時常會看到我在文章中使用物件這個詞,說到底,物件是什麼?物件可以是具體或抽象的事物,其中包含了這件事物的身分特性(屬性)和它會有的行為、功能(方法),打個比方:
將兔子視為物件,而牠的毛色、體型、個性則是能夠形容兔子這個物件的屬性,一種辨別身分的象徵。兔子會有的行為:跑、跳、跺腳、磨牙,則是物件的方法。同時物件是動態的,兔子會隨著成長而改變狀態,體型會從小變大,屬性通常用來描述物件目前的狀態,而方法則代表物件能夠執行的行為。
OOP的特色就是將物件視為單獨個體,能夠被呼叫使用,其中有三個很重要的核心概念:
如果有點程式基礎的人,一定看過有些文章將class比喻為一張「藍圖」,它能夠描繪物件的屬性和方法、定義屬性的型別等,真正透過new建立出來的物件,則可以想像成按照這張藍圖打造出來的實例,就像我們不可能把A1藍圖折成24坪的房子,讓一對新婚夫妻入住進去,世上真有這種事我也不會在這裡寫文章了。
離題了
總之,Class類別是物件導向的基礎。
當我們使用new建立Class的實例時,如果有多筆物件需要創建,不可能這樣寫:
class Animal {
coatColor: string;
age: number;
}
let rabbit = new Animal();
rabbit.coatColor = "white";
rabbit.age = 2;
如果我需要創十個物件,不可能這樣寫30行,如果這十筆物件的性質一樣,我們能夠用constructor簡化它:
class Animal {
coatColor: string;
age: number;
//先定義這個類別需要的建構子
constructor(coatColor: string, age: number) {
this.coatColor = coatColor;
this.age = age;
} //當使用new關鍵字實例化物件時,constructor會執行大括號內的程式
}
let rabbit = new Animal("white",2);
//創建物件時,就能順便寫好初始值了
有一點需要注意:constructor不能像一般函式一樣宣告回傳型別;建立實例時會自動執行 constructor,而它本身不會讓我們取得自訂的回傳值。
另外,可能有人會對this有疑問,以this.age = age;為範例簡單解釋一下:
左邊的this.age是「當前物件的age」,在我們創建rabbit時,指的就是rabbit的age
右邊的age則是constructor傳進來的參數。
接下來介紹常見的存取修飾詞:Public、Private和Protected
存取修飾詞是什麼?存取修飾詞決定了類別、物件、方法能不能被其他程式碼呼叫、使用的範圍,常見的有我們要介紹的這三種。
Public代表屬性或方法能夠從外部直接存取,沒有任何限制。如果沒有特別寫Public的話,class的預設都是它。
class Animal {
public coatColor: string;
public age: number;
constructor(coatColor: string, age: number) {
this.coatColor = coatColor;
this.age = age;
}
}
let rabbit = new Animal("white",2);
console.log(rabbit.coatColor); //合法
console.log(rabbit.age); //合法
Private只有class內部能夠直接存取,外部程式碼不能直接訪問。通常可以用來隱藏不希望外部直接操作的內部資料。
class Animal {
public coatColor: string;
private age: number;
constructor(coatColor: string, age: number) {
this.coatColor = coatColor;
this.age = age;
}
}
let rabbit = new Animal("white",2);
console.log(rabbit.coatColor); //合法
console.log(rabbit.age); //因為外部沒有執行、更動這項屬性的權限,TS會報錯喔!
比起Public和Private兩個極端的範圍,Protected反而趨於保守,不像Public誰都能從外部存取,也不像Private只有內部能夠使用。一般來說,Protected不會給外部存取,但繼承它的子類別能夠存取它的屬性及方法。
class Animal {
protected coatColor: string;
protected age: number;
constructor(coatColor: string, age: number) {
this.coatColor = coatColor;
this.age = age;
}
}
class Rabbit extends Animal {
birthday() {
this.age += 1;
console.log(`兔子過生日了!現在是 ${this.age} 歲。`);
}
}
let rabbit = new Rabbit("white", 2);
rabbit.birthday();
如果希望資料只供檢閱,不能更動,能夠使用readonly來實現:
class Animal {
public coatColor: string;
readonly age: number;
constructor(coatColor: string, age: number) {
this.coatColor = coatColor;
this.age = age;
}
}
let rabbit = new Animal("white",2);
console.log(rabbit.age); //合法,唯讀狀態可以正常讀取
rabbit.age = 3; //不合法,唯讀狀態下的屬性不能被重新賦值
readonly不代表這個值永遠不能改,而是TypeScript不允許它在初始化後再次被重新賦值。Class的readonly成員通常可以在constructor中初始化。
前面說了private的成員無法從外部直接存取,假設我們希望讓外部程式碼能夠取得或修改這個資料,可以使用getter和setter,並在內部控制實際資料的存取方式。
class Animal {
private _age: number;
constructor(age: number) {
this._age = age;
}
get age(): number {
return this._age;
} // get 可以從外部取得 age 的數值
set age(value: number) {
this._age = value;
} // set 可以從外部修改 age 的數值
}
let rabbit = new Animal(2);
console.log(rabbit.age); //輸出:2
rabbit.age = 4; //透過setter修改 _age
console.log(rabbit.age); //輸出:4
class這篇是目前為止寫過最長的文章,畢竟是十分重要的物件導向觀念,在程式上會很常使用到裡面講到的概念,希望大家在看完後能夠有所收穫,今天先寫到這裡,大家拜拜!